package com.lang.thread.synchronizedd;

import org.junit.Test;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * synchronized 学习
 * http://www.importnew.com/21866.html
 */
public class SynchronizedDemo {

    /*
    synchronized修饰一个方法  . synchronized关键字不能继承。
    无论synchronized关键字加在方法上还是对象上，如果它作用的对象是非静态的，则它取得的锁是对象；如果synchronized作用的对象是一个静态方法或一个类，则它取得的锁是对类，该类所有的对象同一把锁
      public synchronized void method()
      {
         // todo
      }
      等价于
      public void method()
      {
         synchronized(this) {
            // todo
         }
      }
     */

    /**
     * 查看一个对象是否被锁住
     * 对象头包含这个对象的一些元数据, 包括两个部分：一个mark word字段，一个klass字段
     * mark word中包含一个锁标志，当对象没有被锁住的时候，标志位为0，否则为1。
     * <p>
     * For 32 bit JVM:
     * _mark    : 4 byte constant
     * _klass    : 4 byte pointer to class
     * <p>
     * For 64 bit JVM:
     * _mark    : 8 byte constant
     * _klass    : 8 byte pointer to class
     * <p>
     * For 64 bit JVM with compressed-oops:
     * _mark    : 8 byte constant
     * _klass    : 4 byte pointer to class
     * <p>
     * https://blog.csdn.net/zhoufanyang_china/article/details/54601311 关于对象头信息学习
     */
    @Test
    public void test01() throws NoSuchFieldException, IllegalAccessException {
        Object lock = new Object();

//        int flag = Unsafe.getUnsafe().getInt(lock,0L);//不能直接获取unsafe对象
        // 通过反射得到theUnsafe对应的Field对象
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        // 设置该Field为可访问
        field.setAccessible(true);
        // 通过Field得到该Field对应的具体对象，传入null是因为该Field为static的
        Unsafe unsafe = (Unsafe) field.get(null);

//        unsafe.object
        Long flag = unsafe.getLong(lock, 0L);
        System.out.println(Long.toBinaryString(flag));
        synchronized (lock) {
            flag = unsafe.getLong(lock, 0L);
            //10100100101110001001100000
            //10100100101110001100000000

            System.out.println(Long.toBinaryString(flag));
        }
        flag = unsafe.getLong(lock, 0L);
        System.out.println(Long.toBinaryString(flag));
    }

    /*
    synchronized 字节码分析 https://www.jianshu.com/p/c5058b6fe8e5
    */


    /**
     * 当对象同步一个代码块，其 属性对象也同步一个代码块，这两个代码块之间是否互斥
     * synchronize 同步对象，只和对象有关系，不约束其子对象，各管各的
     * O类里面的test3方法应该也要对this加锁的，因此多个地方加锁，一个地方没有加锁，也会出现线程安全问题
     */
    @Test
    public void test02() throws InterruptedException {
        O o = new O();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    o.test1();
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        Thread.sleep(500L);
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                o.test2();
            }
        });
        t2.start();

        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                o.test3();
            }
        });
        t3.start();
        Thread.sleep(3500);
        System.out.println(o.list);

    }

    class O {
        List list = new ArrayList();

        public void test1() throws InterruptedException {
            synchronized (this) {
                System.out.println("this 对象以加锁");
                Thread.sleep(2000);
                list.add(1);
                System.out.println(Thread.currentThread().getId()+" test01"+list);            }
        }

        public void test2() {
            System.out.println("into test2");
            synchronized (list) {
                list.add(2);
                System.out.println(Thread.currentThread().getId()+" test02"+list);
            }
        }
        public void test3() {
            System.out.println("into test3");
            this.list = new ArrayList();
            System.out.println("test3 "+list);
        }
    }


    /**
     * 锁嵌套关系， 代码块1 中包含代码块2  两个代码块使用相同的锁同步
     * 因为 偏向锁 和线程有关，这个线程获取了锁，然后后面的锁也是这个线程的，所以不会再次去获取锁。
     */
    @Test
    public void test03(){
        Object lock = new Object();
        synchronized (lock){
            System.out.println("code block 1");
            synchronized (lock){
                System.out.println("code block 2");
            }
        }
    }
}
